---
title: Relations
description: Adding relations to prism objects
---

You can add fields for relations using the `t.relation` method:

```typescript
builder.queryType({
  fields: (t) => ({
    me: t.prismaField({
      type: 'User',
      resolve: async (query, root, args, ctx, info) =>
        prisma.user.findUniqueOrThrow({
          ...query,
          where: { id: ctx.userId },
        }),
    }),
  }),
});

builder.prismaObject('User', {
  fields: (t) => ({
    id: t.exposeID('id'),
    email: t.exposeString('email'),
    posts: t.relation('posts'),
  }),
});

builder.prismaObject('Post', {
  fields: (t) => ({
    id: t.exposeID('id'),
    title: t.exposeString('title'),
    author: t.relation('author'),
  }),
});
```

`t.relation` defines a field that can be pre-loaded by a parent resolver. This will create something
like `{ include: { author: true }}` that will be passed as part of the `query` argument of a
`prismaField` resolver. If the parent is another `relation` field, the includes will become nested,
and the full relation chain will be passed to the `prismaField` that started the chain.

For example the query:

```graphql
query {
  me {
    posts {
      author {
        id
      }
    }
  }
}
```

the `me` `prismaField` would receive something like the following as its query parameter:

```typescript
{
  include: {
    posts: {
      include: {
        author: true;
      }
    }
  }
}
```

This will work perfectly for the majority of queries. There are a number of edge cases that make it
impossible to resolve everything in a single query. When this happens Pothos will automatically
construct an additional query to ensure that everything is still loaded correctly, and split into as
few efficient queries as possible. This process is described in more detail below

### Fallback queries

There are some cases where data can not be pre-loaded by a prisma field. In these cases, pothos will
issue a `findUnique` query for the parent of any fields that were not pre-loaded, and select the
missing relations so those fields can be resolved with the correct data. These queries should be
very efficient, are batched by pothos to combine requirements for multiple fields into one query,
and batched by Prisma to combine multiple queries (in an n+1 situation) to a single sql query.

The following are some edge cases that could cause an additional query to be necessary:

- The parent object was not loaded through a field defined with `t.prismaField`, or `t.relation`
- The root `prismaField` did not correctly spread the `query` arguments in is prisma call.
- The query selects multiple fields that use the same relation with different filters, sorting, or
  limits
- The query contains multiple aliases for the same relation field with different arguments in a way
  that results in different query options for the relation.
- A relation field has a query that is incompatible with the default includes of the parent object

All of the above should be relatively uncommon in normal usage, but the plugin ensures that these
types of edge cases are automatically handled when they do occur.

### Filters, Sorting, and arguments

So far we have been describing very simple queries without any arguments, filtering, or sorting. For
`t.prismaField` definitions, you can add arguments to your field like normal, and pass them into
your prisma query as needed. For `t.relation` the flow is slightly different because we are not
making a prisma query directly. We do this by adding a `query` option to our field options. Query
can either be a query object, or a method that returns a query object based on the field arguments.

```typescript
builder.prismaObject('User', {
  fields: (t) => ({
    id: t.exposeID('id'),
    posts: t.relation('posts', {
      // We can define arguments like any other field
      args: {
        oldestFirst: t.arg.boolean(),
      },
      // Then we can generate our query conditions based on the arguments
      query: (args, context) => ({
        orderBy: {
          createdAt: args.oldestFirst ? 'asc' : 'desc',
        },
      }),
    }),
  }),
});
```

The returned query object will be added to the include section of the `query` argument that gets
passed into the first argument of the parent `t.prismaField`, and can include things like `where`,
`skip`, `take`, and `orderBy`. The `query` function will be passed the arguments for the field, and
the context for the current request. Because it is used for pre-loading data, and solving n+1
issues, it can not be passed the `parent` object because it may not be loaded yet.

```typescript
builder.prismaObject('User', {
  fields: (t) => ({
    id: t.exposeID('id'),
    email: t.exposeString('email'),
    posts: t.relation('posts', {
      // We can define arguments like any other field
      args: {
        oldestFirst: t.arg.boolean(),
      },
      // Then we can generate our query conditions based on the arguments
      query: (args, context) => ({
        orderBy: {
          createdAt: args.oldestFirst ? 'asc' : 'desc',
        },
      }),
    }),
  }),
});
```

### relationCount

Prisma supports querying for
[relation counts](https://www.prisma.io/docs/concepts/components/prisma-client/aggregation-grouping-summarizing#count-relations)
which allow including counts for relations along side other `includes`. Before prisma 4.2.0, this
does not support any filters on the counts, but can give a total count for a relation. Starting from
prisma 4.2.0, filters on relation count are available under the `filteredRelationCount` preview
feature flag.

```typescript
builder.prismaObject('User', {
  fields: (t) => ({
    id: t.exposeID('id'),
    postCount: t.relationCount('posts', {
      where: {
        published: true,
      },
    }),
  }),
});
```
